package com.wzcl.app.service.impl;

import com.wzcl.app.controller.ActivityController;
import com.wzcl.app.dao.model.*;
import com.wzcl.app.dao.repository.ActivityRepository;
import com.wzcl.app.model.common.BusinessException;
import com.wzcl.app.model.common.CodeMsg;
import com.wzcl.app.model.common.MyPage;
import com.wzcl.app.model.common.WZCL;
import com.wzcl.app.model.dto.ActivityDto;
import com.wzcl.app.service.*;
import com.wzcl.app.service.common.activity.ActivityPromotionFactory;
import com.wzcl.app.utils.MD5Util;
import com.wzcl.app.utils.RedisUtil;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.*;
import java.util.*;

@Log4j2
@Service
@PropertySource({"classpath:application-config.properties"})
public class ActivityServiceImpl implements ActivityService {
    private static final String SECKILL_URL_SECRET_KEY = "Qpz%#XiLYn";
    public static final String ON_LINE_KEY = "ON_LINE_KEY_";
    public static final String OFF_LINE_KEY = "OFF_LINE_KEY_";

    @Value("${publicNetIp}")
    private String publicNetIp;

    @Autowired
    private ActivityRepository activityRepository;

    @Autowired
    private ActivitySinglePromotionService singlePromotionService;

    @Autowired
    private ActivitySeckillPromotionService seckillPromotionService;

    @Autowired
    private ActivityFullCutPromotionService fullCutPromotionService;

    @Autowired
    private ActivityGroupPromotionService groupPromotionService;

    @Autowired
    private ActivityRuleService activityRuleService;

    @Autowired
    private RedisUtil redisUtil;

    @Override
    public Activity addActivity(ActivityDto activityDto) {
        if (activityDto.getType().equals(WZCL.ACTIVITY_TYPE.SECKILL) && activityDto.getTime() == null) {
            throw new BusinessException(CodeMsg.MUST_BE_NOT_NULL, "秒杀活动必须有开始和结束时间");
        }
        Activity activity = new Activity();
        BeanUtils.copyProperties(activityDto, activity, "id");
        if (activityDto.getTime() != null && activityDto.getTime().length > 0) {
            activity.setStartTime(activityDto.getTime()[0]);
            activity.setEndTime(activityDto.getTime()[1]);
        }
        activity.setActivityState(WZCL.ACTIVITY_STATE.NOT_ONLINE);
        return activityRepository.save(activity);
    }

    @Override
    public Activity delActivityById(Integer id) {
        Activity activity = getAndCheckById(id);
        if (WZCL.ACTIVITY_STATE.BE_ONLINE.equals(activity.getActivityState()) || WZCL.ACTIVITY_STATE.ONLINE.equals(activity.getActivityState())) {
            throw new BusinessException(CodeMsg.ACTIVITY_CANNOT_OPERATE);
        }
        //删除所有优惠
        getFactoryByType(activity.getType()).deletePromotionByActivityId(activity.getId());
        activityRuleService.delActivityRuleByActivityId(activity.getId());
        log.info("Activity ID:{} physical deletion", id);
        activityRepository.delete(activity);
        return activity;
    }

    @Override
    public Activity updateActivity(ActivityDto activityDto) {
        Activity activity = getAndCheckById(activityDto);
        //仅允许未上线的活动可修改
        if (!WZCL.ACTIVITY_STATE.NOT_ONLINE.equals(activity.getActivityState())) {
            throw new BusinessException(CodeMsg.ACTIVITY_CANNOT_OPERATE);
        }
        BeanUtils.copyProperties(activityDto, activity, "id", "state", "activityState");
        if (activityDto.getTime() != null && activityDto.getTime().length > 0) {
            activity.setStartTime(activityDto.getTime()[0]);
            activity.setEndTime(activityDto.getTime()[1]);
        }
        return activityRepository.save(activity);
    }

    @Override
    public Activity getActivityById(Integer id) {
        return activityRepository.findById(id).orElse(null);
    }

    @Override
    public List<Activity> getActivityById(List<Integer> ids) {
        return activityRepository.findAllById(ids);
    }

    @Override
    public Activity getAndCheckById(Integer id) {
        if (id == null) {
            throw new BusinessException(CodeMsg.MUST_BE_NOT_NULL, "缺少活动Id");
        }
        Activity activity = getActivityById(id);
        if (activity == null) {
            throw new BusinessException(CodeMsg.ACTIVITY_NOT_EXIST);
        }
        return activity;
    }


    @Override
    public List<Activity> getActivityList(ActivityDto activityDto) {
        return activityRepository.findAll(getSpecification(activityDto), activityDto.getSort());
    }

    @Override
    public MyPage<Activity> getActivityPage(ActivityDto activityDto) {
        Page<Activity> all = activityRepository.findAll(getSpecification(activityDto), activityDto.getPageable());
        ArrayList<Activity> list = new ArrayList<>(all.getContent());
        Collections.reverse(list);
        MyPage<Activity> myPage = new MyPage<>();
        myPage.setContent(list);
        myPage.setTotalElements(all.getTotalElements());
        return myPage;
    }

    /**
     * 发布/关闭/预发布活动
     *
     * @param activityDto
     * @return
     */
    @Override
    public void publishActivity(ActivityDto activityDto) {
        Integer state = activityDto.getActivityState();
        if (state == null) {
            throw new BusinessException(CodeMsg.MUST_BE_NOT_NULL, "缺少活动发布状态");
        }
        Activity activity = getAndCheckById(activityDto.getId());
        if (state.equals(activity.getActivityState())) {
            log.info("Activity.activityState not change,pass this request");
            return;
        }
        //选择校验
        if (WZCL.ACTIVITY_STATE.ONLINE.equals(state)) {
            //活动上线的校验校验
            if (activity.getStartTime() != null) {
                if (activity.getStartTime().after(new Date())) {
                    throw new BusinessException(CodeMsg.ACTIVITY_STATE_ERROR, "未到活动时间！！！");
                }
                if (activity.getEndTime().before(new Date())) {
                    throw new BusinessException(CodeMsg.ACTIVITY_STATE_ERROR, "已过活动时间！！！");
                }
                onlineActivity(activity);
                //设置自动下线任务
                long now = System.currentTimeMillis() / 1000;
                long end = activity.getEndTime().getTime() / 1000;
                redisUtil.set(OFF_LINE_KEY + activity.getId(), activity.getName(), end - now);
            } else {
                onlineActivity(activity);
            }
        } else if (WZCL.ACTIVITY_STATE.OFFLINE.equals(state)) {
            offlineActivity(activity);
        } else if (WZCL.ACTIVITY_STATE.BE_ONLINE.equals(state)) {
            if (activity.getStartTime() == null || !activity.getStartTime().after(new Date())) {
                throw new BusinessException(CodeMsg.ACTIVITY_STATE_ERROR, "活动无法预上线！！！");
            }
            beOnlineActivity(activity);
            long now = System.currentTimeMillis() / 1000;
            long start = activity.getStartTime().getTime() / 1000;
            redisUtil.set(ON_LINE_KEY + activity.getId(), activity.getName(), start - now);
        } else {
            throw new BusinessException(CodeMsg.ACTIVITY_STATE_ERROR);
        }
        activity.setActivityState(state);
        activityRepository.save(activity);
        if(WZCL.ACTIVITY_TYPE.SINGLE.equals(activity.getType())){

        }
    }

    //活动上线
    public void onlineActivity(Activity activity) {
            Integer activityId = activity.getId();
            if (WZCL.ACTIVITY_STATE.NOT_ONLINE.equals(activity.getActivityState()) || WZCL.ACTIVITY_STATE.BE_ONLINE.equals(activity.getActivityState())) {
                //未上线 --> 上线||预上线 --> 上线
                if(WZCL.ACTIVITY_TYPE.SECKILL.equals(activity.getType())) {
                    //秒杀活动的上线处理
                    List<Commodity> commodityList = activity.getCommodityList();
                    if (commodityList != null && commodityList.size() > 0) {
                        for (Commodity commodity : commodityList) {
                            Integer commodityId = commodity.getId();
                            //1.设置活动库存数
                            List<ActivitySeckillPromotion> seckillPromotionList = (List<ActivitySeckillPromotion>) getFactoryByType(WZCL.ACTIVITY_TYPE.SECKILL).findAllPromotion(activityId);
                            if(seckillPromotionList.size()>0) {
                                redisUtil.set(ActivityController.SECKILL_STOCK_KEY + commodityId + "_" + activityId, seckillPromotionList.get(0).getStock());
                            }else{
                                throw new BusinessException(CodeMsg.ACTIVITY_CANNOT_OPERATE,"当前秒杀活动缺少一个秒杀优惠");
                            }
                            //2.活动地址
                            String sign = MD5Util.toMD5(SECKILL_URL_SECRET_KEY + commodityId + SECKILL_URL_SECRET_KEY + activityId);
                            String url = String.format("%s/activity/seckill?cid=%d&aid=%d&sign=%s", publicNetIp, commodityId, activityId, sign);
                            redisUtil.set(ActivityController.SECKILL_URL_KEY + commodityId + "_" + activityId, url);
                        }
                    }
                }
            } else if (WZCL.ACTIVITY_STATE.OFFLINE.equals(activity.getActivityState())) {
                //已下线 --> 上线
                throw new BusinessException(CodeMsg.ACTIVITY_STATE_ERROR, "下线活动上线开发中！！！");
            } else {
                throw new BusinessException(CodeMsg.ACTIVITY_STATE_ERROR);
            }
        log.info("活动ID:{}启动成功", activityId);
    }

    //关闭活动
    public void offlineActivity(Activity activity) {
        Integer activityId = activity.getId();
        if (WZCL.ACTIVITY_STATE.ONLINE.equals(activity.getActivityState())) {
            //上线 --> 下线
            if (WZCL.ACTIVITY_TYPE.SECKILL.equals(activity.getType())) {
                //秒杀活动的下线处理
                List<Commodity> commodityList = activity.getCommodityList();
                if (commodityList != null && commodityList.size() > 0) {
                    for (Commodity commodity : commodityList) {
                        //1.删除活动地址,删除活动库存数
                        Integer commodityId = commodity.getId();
                        redisUtil.del(ActivityController.SECKILL_URL_KEY + commodityId + "_" + activityId,
                                ActivityController.SECKILL_STOCK_KEY + commodityId + "_" + activityId);
                    }
                }
            }
        } else if (WZCL.ACTIVITY_STATE.BE_ONLINE.equals(activity.getActivityState())) {
            //预上线 --> 下线
        } else if (WZCL.ACTIVITY_STATE.NOT_ONLINE.equals(activity.getActivityState())) {
            //未上线 --> 下线
            throw new BusinessException(CodeMsg.ACTIVITY_STATE_ERROR, "未上线活动无需下线！！！");
        } else {
            throw new BusinessException(CodeMsg.ACTIVITY_STATE_ERROR);
        }
        log.info("活动ID:{}关闭成功", activity.getId());
    }

    //预上线活动
    public void beOnlineActivity(Activity activity) {
        if (WZCL.ACTIVITY_STATE.NOT_ONLINE.equals(activity.getActivityState())) {
            //未上线 --> 预上线
        } else if (WZCL.ACTIVITY_STATE.OFFLINE.equals(activity.getActivityState())) {
            //已下线 --> 预上线
            throw new BusinessException(CodeMsg.ACTIVITY_STATE_ERROR, "下线活动预上线开发中！！！");
        } else if (WZCL.ACTIVITY_STATE.ONLINE.equals(activity.getActivityState())) {
            //上线 --> 预上线
            throw new BusinessException(CodeMsg.ACTIVITY_STATE_ERROR, "上线活动无法预上线！！！");
        } else {
            throw new BusinessException(CodeMsg.ACTIVITY_STATE_ERROR);
        }
        log.info("活动ID:{}预上线成功", activity.getId());
    }


    /**
     * 校验用户能否参加当前活动
     * @param user     用户信息
     * @param activity 活动
     * @param data     用于存放用户参数（多活动共享）
     * @return
     */
    @Override
    public Boolean checkActivity(AppUser user, Activity activity, Map<String, Object> data) {
        Boolean flag = true;
        if (!WZCL.ACTIVITY_STATE.ONLINE.equals(activity.getActivityState())) {
            return false;
        }
        List<ActivityRule> rules = activityRuleService.getActivityRuleListByActivityId(activity.getId());
        if (rules != null && rules.size() > 0) {
            for (ActivityRule rule : rules) {
                flag = activityRuleService.checkRule(user, rule, data);
                if (!flag) {
                    break;
                }
            }
        }
        return flag;
    }


    @Override
    public boolean checkSign(String commodityId, String activityId, String sign) {
        try {
            return sign.equals(MD5Util.toMD5(SECKILL_URL_SECRET_KEY + commodityId + SECKILL_URL_SECRET_KEY + activityId));
        } catch (Exception e) {
        }
        return false;
    }


    public ActivityPromotionFactory getFactoryByType(Integer type) {
        if (WZCL.ACTIVITY_TYPE.SINGLE.equals(type)) return new ActivityPromotionFactory(this, singlePromotionService);
        if (WZCL.ACTIVITY_TYPE.FULL_CUT.equals(type)) return new ActivityPromotionFactory(this, fullCutPromotionService);
        if (WZCL.ACTIVITY_TYPE.SECKILL.equals(type)) return new ActivityPromotionFactory(this, seckillPromotionService);
        if (WZCL.ACTIVITY_TYPE.GROUP.equals(type)) return new ActivityPromotionFactory(this, groupPromotionService);
        throw new BusinessException(CodeMsg.ACTIVITY_TYPE_ERROR);
    }

    private Specification<Activity> getSpecification(ActivityDto activityDto) {
        return new Specification<Activity>() {
            @Override
            public Predicate toPredicate(Root<Activity> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = new ArrayList<Predicate>();
                if (!StringUtils.isBlank(activityDto.getName())) {
                    predicates.add(criteriaBuilder.like(root.get("name"), "%" + activityDto.getName() + "%"));
                }
                if (activityDto.getType() != null) {
                    predicates.add(criteriaBuilder.equal(root.get("type"), activityDto.getType()));
                }
                if (activityDto.getActivityState() != null) {
                    predicates.add(criteriaBuilder.equal(root.get("activityState"), activityDto.getActivityState()));
                }
                if (activityDto.getCommodityId() != null) {
                    Join<Activity, Commodity> commodityPath = root.join(root.getModel().getList("commodityList", Commodity.class), JoinType.LEFT);
                    predicates.add(criteriaBuilder.equal(commodityPath.get("id"), activityDto.getCommodityId()));
                }
                return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
            }
        };
    }
}
